package com.huatai.common.httputil;

/**
 * @program: ht-system
 * @description:
 * @author: songxiude
 * @create: 2023-04-25 15:44
 **/

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.annotation.Nullable;

import com.huatai.common.util.ConvertUtil;
import com.huatai.common.util.StringUtil;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.TextNode;
import org.jsoup.select.Elements;
import org.jsoup.select.Selector;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.TypeDescriptor;

public class CssQueryMethodInterceptor implements MethodInterceptor {
	private final Class<?> clazz;
	private final Element element;

	@Nullable
	public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		String name = method.getName();
		if (!StringUtil.startsWithIgnoreCase(name, "GET")) {
			return methodProxy.invokeSuper(object, args);
		} else {
			Field field = this.clazz.getDeclaredField(StringUtil.firstCharToLower(name.substring(3)));
			CssQuery cssQuery = (CssQuery)field.getAnnotation(CssQuery.class);
			if (cssQuery == null) {
				return methodProxy.invokeSuper(object, args);
			} else {
				Class<?> returnType = method.getReturnType();
				boolean isColl = Collection.class.isAssignableFrom(returnType);
				String cssQueryValue = cssQuery.value();
				boolean isInner = cssQuery.inner();
				if (isInner) {
					return this.proxyInner(cssQueryValue, method, returnType, isColl);
				} else {
					Object proxyValue = this.proxyValue(cssQueryValue, cssQuery, returnType, isColl);
					if (String.class.isAssignableFrom(returnType)) {
						return proxyValue;
					} else {
						TypeDescriptor typeDescriptor = new TypeDescriptor(field);
						return ConvertUtil.convert(proxyValue, typeDescriptor);
					}
				}
			}
		}
	}

	@Nullable
	private Object proxyValue(String cssQueryValue, CssQuery cssQuery, Class<?> returnType, boolean isColl) {
		if (isColl) {
			Elements elements = Selector.select(cssQueryValue, this.element);
			Collection<Object> valueList = this.newColl(returnType);
			if (elements.isEmpty()) {
				return valueList;
			} else {
				Iterator var7 = elements.iterator();

				while(var7.hasNext()) {
					Element select = (Element)var7.next();
					String value = this.getValue(select, cssQuery);
					if (value != null) {
						valueList.add(value);
					}
				}

				return valueList;
			}
		} else {
			Element select = Selector.selectFirst(cssQueryValue, this.element);
			return this.getValue(select, cssQuery);
		}
	}

	private Object proxyInner(String cssQueryValue, Method method, Class<?> returnType, boolean isColl) {
		if (!isColl) {
			Element select = Selector.selectFirst(cssQueryValue, this.element);
			return DomMapper.readValue(select, returnType);
		} else {
			Elements elements = Selector.select(cssQueryValue, this.element);
			Collection<Object> valueList = this.newColl(returnType);
			ResolvableType resolvableType = ResolvableType.forMethodReturnType(method);
			Class<?> innerType = resolvableType.getGeneric(new int[]{0}).resolve();
			if (innerType == null) {
				throw new IllegalArgumentException("Class " + returnType + " 读取泛型失败。");
			} else {
				Iterator var9 = elements.iterator();

				while(var9.hasNext()) {
					Element select = (Element)var9.next();
					valueList.add(DomMapper.readValue(select, innerType));
				}

				return valueList;
			}
		}
	}

	@Nullable
	private String getValue(@Nullable Element element, CssQuery cssQuery) {
		if (element == null) {
			return null;
		} else {
			String attrName = cssQuery.attr();
			String attrValue;
			if (StringUtil.isBlank(attrName)) {
				attrValue = element.outerHtml();
			} else if ("html".equalsIgnoreCase(attrName)) {
				attrValue = element.html();
			} else if ("text".equalsIgnoreCase(attrName)) {
				attrValue = this.getText(element);
			} else if ("allText".equalsIgnoreCase(attrName)) {
				attrValue = element.text();
			} else {
				attrValue = element.attr(attrName);
			}

			String regex = cssQuery.regex();
			return !StringUtil.isBlank(attrValue) && !StringUtil.isBlank(regex) ? this.getRegexValue(regex, cssQuery.regexGroup(), attrValue) : attrValue;
		}
	}

	@Nullable
	private String getRegexValue(String regex, int regexGroup, String value) {
		Matcher matcher = Pattern.compile(regex, 34).matcher(value);
		if (!matcher.find()) {
			return null;
		} else {
			return regexGroup > 0 ? matcher.group(regexGroup) : matcher.group();
		}
	}

	private String getText(Element element) {
		return (String)element.childNodes().stream().filter((node) -> {
			return node instanceof TextNode;
		}).map((node) -> {
			return (TextNode)node;
		}).map(TextNode::text).collect(Collectors.joining());
	}

	private Collection<Object> newColl(Class<?> returnType) {
		return (Collection)(Set.class.isAssignableFrom(returnType) ? new HashSet() : new ArrayList());
	}

	public CssQueryMethodInterceptor(final Class<?> clazz, final Element element) {
		this.clazz = clazz;
		this.element = element;
	}
}

